using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Microsoft.AspNetCore.DataProtection;
using System.Diagnostics;
using System.Security.Cryptography;

var type = ProtectType.Ephemeral;
if (type == ProtectType.Normal)
{
    var originalPayload = Guid.NewGuid().ToString();
    var protectedPayload = Encrypt("foo", originalPayload);
    var unprotectedPayload = Decrypt("foo", protectedPayload);
    Console.WriteLine("{0}\n{1}\n{2}", originalPayload, protectedPayload, unprotectedPayload);
    Debug.Assert(originalPayload == unprotectedPayload);

    static string Encrypt(string purpose, string originalPayload) => GetDataProtector(purpose).Protect(originalPayload);
    static string Decrypt(string purpose, string protectedPayload) => GetDataProtector(purpose).Unprotect(protectedPayload);

    static IDataProtector GetDataProtector(string purpose)
    {
        var services = new ServiceCollection();
        services.AddDataProtection();
        return services
            .BuildServiceProvider()
            .GetRequiredService<IDataProtectionProvider>()
            .CreateProtector(purpose);
    }
}
else if (type == ProtectType.ExpireTime)
{
    var originalPayload = Guid.NewGuid().ToString();
    var protectedPayload = Encrypt("foo", originalPayload, TimeSpan.FromSeconds(5));

    var unprotectedPayload = Decrypt("foo", protectedPayload);
    Debug.Assert(originalPayload == unprotectedPayload);

    await Task.Delay(5000);
    Decrypt("foo", protectedPayload);

    static string Encrypt(string purpose, string originalPayload, TimeSpan timeout)
        => GetDataProtector(purpose)
            .Protect(originalPayload, DateTimeOffset.UtcNow.Add(timeout));
    static string Decrypt(string purpose, string protectedPayload)
        => GetDataProtector(purpose).Unprotect(protectedPayload, out _);

    static ITimeLimitedDataProtector GetDataProtector(string purpose)
    {
        var services = new ServiceCollection();
        services.AddDataProtection();
        return services
            .BuildServiceProvider()
            .GetDataProtector(purpose)
            .ToTimeLimitedDataProtector();
    }

}
else if (type == ProtectType.Ephemeral)
{
    var originalPayload = Guid.NewGuid().ToString();
    var dataProtectionProvider = CreateEphemeralDataProtectionProvider();
    var protector = dataProtectionProvider.CreateProtector("foobar");
    var protectedPayload = protector.Protect(originalPayload);

    protector = dataProtectionProvider.CreateProtector("foobar");
    Debug.Assert(originalPayload == protector.Unprotect(protectedPayload));

    protector = CreateEphemeralDataProtectionProvider().CreateProtector("foobar");
    protector.Unprotect(protectedPayload);

    static IDataProtectionProvider CreateEphemeralDataProtectionProvider()
    {
        var services = new ServiceCollection();
        services.AddDataProtection().UseEphemeralDataProtectionProvider();
        return services.BuildServiceProvider().GetRequiredService<IDataProtectionProvider>();
    }
}
else if (type == ProtectType.Fix)
{
    var password = "123456";
    var salt = new byte[16];
    var iteration = 1000;

    using (var generator = RandomNumberGenerator.Create())
    {
        generator.GetBytes(salt);
    }

    Console.WriteLine(Hash(KeyDerivationPrf.HMACSHA1));
    Console.WriteLine(Hash(KeyDerivationPrf.HMACSHA256));
    Console.WriteLine(Hash(KeyDerivationPrf.HMACSHA512));

    string Hash(KeyDerivationPrf prf)
    {
        var hashed = KeyDerivation.Pbkdf2(
            password: password,
            salt: salt,
            prf: prf,
            iterationCount: iteration,
            numBytesRequested: 32);
        return Convert.ToBase64String(hashed);
    }
}

enum ProtectType
{
    Normal,
    ExpireTime,
    Ephemeral,
    Fix
}